/*
 *  $Id: VTElement.java 6 2008-12-07 07:10:06Z Nathaniel.Waisbrot $
    Copyright (C) 2008  Nathaniel Waisbrot

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package termint.gui.vt;

import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json.JSONException;
import org.json.JSONObject;

public class VTElement implements Serializable, Comparable<VTElement> {
	/** generated */
	private static final long serialVersionUID = -4166390372790900674L;
	
	private final char c;
	private final VT100Color bg, fg;
	private final boolean bold;
	private final boolean underline;
	private final boolean invert;
	private final boolean low;
	private final boolean invisible;
	
	private static final Pattern fromStringPattern =
		Pattern.compile("[{]'(.)':([A-Z]+)/([A-Z]+);(bold|);(underline|);(invert|);(low|);(invisible|)[}]");
	
	private VTElement(char c, VT100Color bg, VT100Color fg, boolean bold, boolean underline, boolean invert,
			boolean low, boolean invisible) {
		this.c = c;
		this.fg = fg;
		this.bg = bg;
		this.bold = bold;
		this.underline = underline;
		this.invert = invert;
		this.low = low;
		this.invisible = invisible;
	}
	
	public VTElement(char character, int attributes) {
		this(character, VT320.getVT100BackgroundColor(attributes),VT320.getVT100ForegroundColor(attributes),
				(attributes & VDUBuffer.BOLD) != 0, (attributes & VDUBuffer.UNDERLINE) != 0,
				(attributes & VDUBuffer.INVERT) != 0, (attributes & VDUBuffer.LOW) != 0,
				(attributes & VDUBuffer.INVISIBLE) != 0);
	}
	
	@Override
	public String toString() {
		if (isNull())
			return "{NULL}";
		StringBuilder s = new StringBuilder();
		s.append("{'"+c+"':");
		s.append(getFg());
		s.append('/');
		s.append(getBg());
		s.append(';');
		if (isBold()) s.append("bold");
		s.append(';');
		if (isUnderline()) s.append("underline");
		s.append(';');
		if (isInvert()) s.append("invert");
		s.append(';');
		if (isLow()) s.append("low");
		s.append(';');
		if (isInvisible()) s.append("invisible");
		s.append('}');
		return s.toString();
	}
	
	public JSONObject toJSON() {
		try {
			JSONObject json = new JSONObject();
			if (!isNull()) {
				json.put("char",Character.toString(c));
				if (!getFg().isNone())
					json.put("fg",getFg().toString());
				if (!getBg().isNone())
					json.put("bg", getBg().toString());
				if (isBold())
					json.put("bold", isBold());
				if (isUnderline())
					json.put("underline", isUnderline());
				if (isInvert())
					json.put("invert", isInvert());
				if (isLow())
					json.put("low", isLow());
				if (isInvisible())
					json.put("invisible", isInvisible());
			}
			return json;
		} catch (JSONException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static VTElement fromJSON(JSONObject json) {
		try {
			return new VTElement(
					json.getString("char").charAt(0),
					VT100Color.valueOf(json.optString("bg", "NONE")),
					VT100Color.valueOf(json.optString("fg", "NONE")),
					json.optBoolean("bold"),
					json.optBoolean("underline"),
					json.optBoolean("invert"),
					json.optBoolean("low"),
					json.optBoolean("invisible")
					);
		} catch (JSONException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static VTElement fromString(String s) {
		Matcher match = fromStringPattern.matcher(s);
		assert match.matches() : "Failed match.  Regex="+fromStringPattern.pattern()+".  String="+s;
		int captures = match.groupCount();
		assert captures == 8 : "VTElements have 8 captures.  Got "+captures;
		return new VTElement(Character.valueOf(match.group(1).charAt(0)),
				VT100Color.valueOf(match.group(2)), VT100Color.valueOf(match.group(3)), 
				match.group(4).length() > 0, match.group(5).length() > 0, match.group(6).length() > 0,
				match.group(7).length() > 0, match.group(8).length() > 0);
	}
	
	@Override
	public int hashCode() {
		return Character.valueOf(c).hashCode();
	}
	
	@Override
	public boolean equals(Object o) {
		if (o instanceof VTElement) {
			return compareTo((VTElement)o) == 0;
		} else
			return false;
	}

	/**
	 * @return the c
	 */
	public char getChar() {
		return c;
	}

	/**
	 * @return the bg
	 */
	public VT100Color getBg() {
		return bg;
	}

	/**
	 * @return the bold
	 */
	public boolean isBold() {
		return bold;
	}

	/**
	 * @return the fg
	 */
	public VT100Color getFg() {
		return fg;
	}

	/**
	 * @return the underline
	 */
	public boolean isUnderline() {
		return underline;
	}

	/**
	 * @return the invert
	 */
	public boolean isInvert() {
		return invert;
	}

	/**
	 * @return the low
	 */
	public boolean isLow() {
		return low;
	}

	/**
	 * @return the invisible
	 */
	public boolean isInvisible() {
		return invisible;
	}
	
	public boolean isNull() {
		return c == '\0';
	}

	public int compareTo(VTElement o) {
		int cmp = Character.compare(c, o.c);
		if (cmp != 0) return cmp;
		cmp = bg.compareTo(o.bg);
		if (cmp != 0) return cmp;
		cmp = fg.compareTo(o.fg);
		if (cmp != 0) return cmp;
		cmp = Boolean.compare(bold, o.bold);
		if (cmp != 0) return cmp;
		cmp = Boolean.compare(invert, o.invert);
		if (cmp != 0) return cmp;
		cmp = Boolean.compare(invisible, o.invisible);
		if (cmp != 0) return cmp;
		cmp = Boolean.compare(low, o.low);
		if (cmp != 0) return cmp;
		cmp = Boolean.compare(underline, o.underline);
		return cmp;
	}
}
